package com.qianfeng.smartdevice.websocket;


//
//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖镇楼            BUG辟易  
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  


import com.qianfeng.smartdevice.event.PowerCommandEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.config.annotation.EnableWebSocket;

import java.io.IOException;

/**
 * Created by jackiechan on 2021/6/10 16:47
 *
 * @author jackiechan
 */
@Component
@EnableWebSocket//开启 websocket
public class PowerCommandWebsocket extends MyBaseWebsocketHandler {
//
//    //我们的目的是通过当前 websocket 给指定的设备推送命令
//    //哪个设备?是一个设备吗? 很多个设备
//    //每个设备的会话是什么?
//    //保存的是所有的设备的会话
//    private Map<String, WebSocketSession> allClients = new HashMap<>();
//
//    private Map<String, Long> lastConnectionTime = new HashMap<>();//记录设备最后一次连接时间的
//
//
//    private ApplicationContext context;
//
//    @Autowired
//    public void setContext(ApplicationContext context) {
//        this.context = context;
//    }
//
//    @Override
//    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//        //当设备和服务器建立连接的时候执行,保存会话
//        //我们约定地址的最后一位是设备的编码
//        String uri = session.getUri().toString();
//        String uuid = uri.substring(uri.lastIndexOf("/") + 1);//获取设备的唯一标识
//        session.getAttributes().put("uuid", uuid);//为了方便后面随时获取 uuid,我们保存了一份
//        allClients.put(uuid, session);
//        System.err.println("收到 websocket 请求了" + uuid);
//        lastConnectionTime.put(uuid, System.currentTimeMillis());//保存设备的连接时间
//        //当前方法执行的时候代表设备连接到服务器了,需要更新状态,需要修改当前设备在数据库中的状态为现在
//        //当前设备是谁?我们要求设备以 uuid 作为标识来连接我们的 websocket,所以上面的 uuid 就是设备的 uuid
//        //我们可以以 uuid作为条件,更新设备的在线状态
//        //更新状态和 websocket 建立连接这两个操作有直接关系吗? 更新数据库失败要不要将设备下线?不需要,也就是说我们只是通过这个方法来知道设备上线而已,更新状态是独立的操作
//        context.publishEvent(DeviceStatusChangeEvent.onLine(uuid));
//    }
//
//    /**
//     * 如果设备是因为断电断网等非正常原因导致的断开连接,服务器不会受到断开的操作,所以该方法在非正常的情况下不会执行
//     * 我们可以通过记录设备的最后一次心跳时间的方式来判断,比如超过一分钟没有发送过心跳数据的我们就认为你掉线了
//     * 所以需要记录设备的最后一次心跳时间,每次心跳都需要更新时间的,什么时候有心跳,我们可以简单的认为心跳就是设备向服务器发送了一个数据
//     * 但是设备什么时候掉线也不确定,在任何时间点都可能会掉线,所以我们只能循环检查这个事情,定时任务
//     *
//     * @param session
//     * @param status
//     * @throws Exception
//     */
//    @Override
//    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
//        String uuid = (String) session.getAttributes().get("uuid");
//        allClients.remove(uuid);//当会话关闭的时候从所有的客户端会话保存的 map 中删除
//        System.err.println("设备断开连接了" + uuid);
//        context.publishEvent(DeviceStatusChangeEvent.offLine(uuid));//更新设备状态为离线
//    }
//
//    /**
//     * 处理文本消息的
//     *
//     * @param session
//     * @param message
//     * @throws Exception
//     */
//    @Override
//    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
//        //发送指令给设备
//        // 收到消息后直接发送指令给设备
//        //我们暂时约定 ,前端传递 {"touser":"shebeiid","msg":"mingling"}
////
////        try {
////            String json = message.getPayload();
////            Map map = objectMapper.readValue(json, Map.class);
////            String touser = (String) map.get("touser");
////            String msg = (String) map.get("msg");//指令
////            WebSocketSession deviceSession = allClients.get(touser);//获取到设备的会话
////            if (deviceSession != null && deviceSession.isOpen()) {
////                //会话是存在的
////                deviceSession.sendMessage(new TextMessage(msg));
////            }
////        } catch (Exception e) {
////            e.printStackTrace();
////        }
//        String uuid = (String) session.getAttributes().get("uuid");//获取到发送心跳数据的设备的 uuid
//        //现在设备没发送一次数据我们就更新一次时间,所以必须有个地方保存了更新时间
//        System.err.println("收到设备 的心跳数据了");
//        lastConnectionTime.put(uuid, System.currentTimeMillis());
//
//    }

    /**
     * 当前方法的做用是在收到设备检查在线状态的事件后检查设备的在线状态,通过定时任务发送事件
     *
     * @param event
     */
//    @EventListener
//    public void onEvent(CheckDeviceStatusTaskEvent event) {
//        System.err.println("websocket收到了检查状态的事件,准备开始检查数据");
//        //遍历保存所有设备的那个 map 集合,看看哪个设备的上次时间超过了我们的阈值
////        lastConnectionTime.forEach((uuid, time) -> {
////
////            //获取当前的时间
////            long currentTimeMillis = System.currentTimeMillis();
////            if (currentTimeMillis - time >= 60000) {
////                //当前时间和上次的心跳时间已经超出了一分钟
////                WebSocketSession session = allClients.get(uuid);
////                allClients.remove(uuid);
////                //不能光只从 map 中删除,还需要关闭连接
////                if (session != null && session.isOpen()) {
////                    try {
////                        session.close();
////                        session = null;
////                    } catch (IOException e) {
////                        e.printStackTrace();
////                    }
////                }
////                lastConnectionTime.remove(uuid);//从当前map 中移除当前设备,下次定时任务的时候就不再遍历这个 uuid 了,因为它已经被删除了,出现了并发问题
////            }
////        });
//
//        //lastConnectionTime.keySet() 返回的是 map 中的一个 set 变量,指向的是同一个对象
////        lastConnectionTime.keySet().forEach(uuid -> {
////            //获取当前的时间
////            long currentTimeMillis = System.currentTimeMillis();
////            Long time = lastConnectionTime.get(uuid);//获取当前设备的上次心跳时间
////
////            if (currentTimeMillis - time >= 60000) {
////                //当前时间和上次的心跳时间已经超出了一分钟
////                WebSocketSession session = allClients.get(uuid);
////                allClients.remove(uuid);//因为设备已经离线了,所以从保存有所有会话的 map 中删除数据
////                //不能光只从 map 中删除,还需要关闭连接
////                if (session != null && session.isOpen()) {
////                    try {
////                        session.close();
////                        session = null;
////                    } catch (IOException e) {
////                        e.printStackTrace();
////                    }
////                }
////                lastConnectionTime.remove(uuid);//从当前map 中移除当前设备,下次定时任务的时候就不再遍历这个 uuid 了,因为它已经被删除了,出现了并发问题
////            }
////
////        });
////        lastConnectionTime.keySet().stream().filter(uuid->{
////
////            //获取当前的时间
////            long currentTimeMillis = System.currentTimeMillis();
////            Long time = lastConnectionTime.get(uuid);//获取当前设备的上次心跳时间
////
////            if (currentTimeMillis - time >= 60000) {
////                //当前时间和上次的心跳时间已经超出了一分钟
////                WebSocketSession session = allClients.get(uuid);
////                allClients.remove(uuid);//因为设备已经离线了,所以从保存有所有会话的 map 中删除数据
////                //不能光只从 map 中删除,还需要关闭连接
////                if (session != null && session.isOpen()) {
////                    try {
////                        session.close();
////                        session = null;
////                    } catch (IOException e) {
////                        e.printStackTrace();
////                    }
////                }
////                lastConnectionTime.remove(uuid);//从当前map 中移除当前设备,下次定时任务的时候就不再遍历这个 uuid 了,因为它已经被删除了,出现了并发问题
////            }
////            return true;
////        }).count();
//        //遍历在线时间的 map 的 uuid,找到所有超出时间的 key,先删掉这个数据,然后将 uuid 放到一个新的集合中返回
//        List<String> list = lastConnectionTime.keySet().stream().filter(uuid -> {
//
//            //获取当前的时间
//            long currentTimeMillis = System.currentTimeMillis();
//            Long time = lastConnectionTime.get(uuid);//获取当前设备的上次心跳时间
//
//            if (currentTimeMillis - time >= 60000) {
//                //当前时间和上次的心跳时间已经超出了一分钟
//                WebSocketSession session = allClients.get(uuid);
//                allClients.remove(uuid);//因为设备已经离线了,所以从保存有所有会话的 map 中删除数据
//                //不能光只从 map 中删除,还需要关闭连接
//                if (session != null && session.isOpen()) {
//                    try {
//                        session.close();//关闭连接,会触发关闭的监听,在内部我们已经做了更新数据的事件
//                        session = null;
//                    } catch (IOException e) {
//                        e.printStackTrace();
//                    }
//                }
//            }
//            return currentTimeMillis - time >= 60000; //只要离线的设备
//        }).collect(Collectors.toList());
//
//        //遍历所有离线的设备的 uuid 集合,然后从lastConnectionTime删除掉指定的数据
//        list.forEach(uuid->{
//            lastConnectionTime.remove(uuid);
//        });
//    }

    @EventListener
    public void onEvent(PowerCommandEvent event) {
        //发送指令的
        //给谁发送指令
        String uuid = event.getUuid();
        //发送什么指令
        String command = event.getCommand();

        WebSocketSession session = allClients.get(uuid);

        if (session != null&&session.isOpen()) {
            try {
                session.sendMessage(new TextMessage(command));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}
